package log

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"runtime"
	"slices"
	"strings"

	"gitee.com/doraemon1996/bunny/utils"
	"golang.org/x/sys/windows"
	"golang.org/x/term"
)

const (
	Color_Black   = "\x1b[30m" // 黑色
	Color_Red     = "\x1b[31m" // 红色
	Color_Green   = "\x1b[32m" // 绿色
	Color_Yellow  = "\x1b[33m" // 黄色
	Color_Blue    = "\x1b[34m" // 蓝色
	Color_Magenta = "\x1b[35m" // 品红
	Color_Cyan    = "\x1b[36m" // 青色
	Color_White   = "\x1b[37m" // 白色
	Color_Default = "\x1b[0m"  // 默认值

	WindowsConsoleMode = windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
)

// 文本日志格式化对象
type TextFormatter struct {
	// 禁用日志数据颜色,优先级高于`ForceColors`
	DisableColors bool

	// 设置为true时,会为日志数据添加颜色的终端控制符
	ForceColors bool

	// 时间格式化模板
	TimestampFormat string

	// 默认字段重定义
	FieldMap FieldMap

	// 自定义调用帧
	CustomCaller CustomCaller
}

func (tf *TextFormatter) isColored(isTerminal bool) bool {
	isColored := tf.ForceColors || isTerminal
	return isColored && !tf.DisableColors
}

func (tf *TextFormatter) levelColor(level Level) string {
	switch level {
	case Level_Trace, Level_Debug:
		return Color_White
	case Level_Info:
		return Color_Green
	case Level_Warn:
		return Color_Yellow
	case Level_Error, Level_Panic, Level_Fatal:
		return Color_Red
	default:
		return Color_Green
	}
}

// 实现Formatter.Format接口
// 使用了颜色标记时,会在这条日志头部添加日志等级对应颜色的ANSI转义码,日志尾部添加默认颜色的转义码
func (tf *TextFormatter) Format(entry *Entry, buf *bytes.Buffer) error {
	isColored := tf.isColored(checkTerminal(entry.Logger.Output))

	data := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		data[k] = v
	}
	prefixFieldClashes(data, tf.FieldMap)
	keys := make([]string, 0, len(data))
	for key := range data {
		keys = append(keys, key)
	}
	slices.Sort(keys)

	if isColored {
		buf.WriteString(tf.levelColor(entry.Level))
	}

	// 时间
	timeFormat := tf.TimestampFormat
	if timeFormat == "" {
		timeFormat = LogTimeFormat
	}
	buf.WriteString(entry.Time.Format(timeFormat))

	// 日志等级
	fmt.Fprintf(buf, " %-5s", strings.ToLower(entry.Level.String()))

	// msg
	if entry.Message != "" {
		fmt.Fprintf(buf, " %s", entry.Message)
	}

	// err
	if entry.err != "" {
		fmt.Fprintf(buf, " %s=%v", LogKey_Error, entry.err)
	}

	if entry.HasCaller() {
		var callerFunc CustomCaller
		if tf.CustomCaller != nil {
			callerFunc = tf.CustomCaller
		} else {
			callerFunc = utils.CallerStringFuncFile
		}
		strFunc, strFile := callerFunc(entry.Caller)

		// 文件
		if strFunc != "" {
			fmt.Fprintf(buf, " %s=%s", LogKey_Func, strFunc)
		}

		// 函数
		if strFile != "" {
			fmt.Fprintf(buf, " %s=%s", LogKey_File, strFile)
		}
	}

	// 自定义字段
	for _, key := range keys {
		fmt.Fprintf(buf, " %s=%#v", key, data[key])
	}

	if isColored {
		buf.WriteString(Color_Default)
	}
	buf.WriteByte('\n')

	return nil
}

// 判断Writer是否是终端
func checkTerminal(w io.Writer) bool {
	switch v := w.(type) {
	case *os.File:
		ok := term.IsTerminal(int(v.Fd()))
		// windows控制台默认不支持ANSI转义码,需要设置模式以开启
		// https://learn.microsoft.com/zh-cn/windows/console/high-level-console-modes
		if ok && runtime.GOOS == "windows" {
			var mode uint32
			handle := windows.Handle(v.Fd())
			if err := windows.GetConsoleMode(handle, &mode); err != nil {
				return false
			}
			if mode&WindowsConsoleMode != WindowsConsoleMode {
				mode |= WindowsConsoleMode
				if err := windows.SetConsoleMode(handle, mode); err != nil {
					return false
				}
			}
		}
		return ok
	}

	return false
}
